/**************************************************************************(C)*
 *
 *  This file is part of the GreenHills project
 *  Copyright (C) 2009  Tieto Corporation
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  Not for commmercial use. Only with Tieto permission.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *************************************************************************(C)*/

#include "../gpscoordinate.h"
#include "util/binstream.h"

#include <stdlib.h>
#include <assert.h>

#include <QtCore/QStringList>

using namespace greenhills::gps;
using namespace greenhills::util;

#if defined Q_OS_LINUX
    #include <math.h>
#elif defined Q_OS_WIN32
    #include <math.h>
#elif defined Q_OS_WINCE
    #define M_PI 3.14159265358979323846
#endif

//1o9N - 018o59.1234N
#define MIN_COORD_TEXT_LENGTH 4
#define MAX_COORD_TEXT_LENGTH 12

const unsigned long COORD_1_MINUTE = 10000;
const unsigned long COORD_1_DEGREE = (60*COORD_1_MINUTE);

CGPSCoordinate::CGPSCoordinate()
    :mCoordinate(0)
{}

CGPSCoordinate::CGPSCoordinate(long pCoordinate)
    :mCoordinate(pCoordinate)
{}

QString CGPSCoordinate::toString(const char *pFormatString,
        char pPosSuffix, char pNegSuffix) const
{
    QString tmp;
    long absCoord = abs(coordinate());
    tmp.sprintf(pFormatString,
        (int)(absCoord/(double)COORD_1_DEGREE),
        (double)(absCoord%COORD_1_DEGREE)/(double)COORD_1_MINUTE);
    tmp += coordinate()<0 ?  pNegSuffix : pPosSuffix;
    return tmp;
}

obinstream& CGPSCoordinate::operator<<(obinstream &pStream) const
{
    pStream << mCoordinate;
    return pStream;
}

ibinstream& CGPSCoordinate::operator>>(ibinstream &pStream)
{
    pStream >> mCoordinate;
    return pStream;
}

bool CGPSCoordinate::operator == (const CGPSCoordinate &pCoordinate)
{
    //! @todo Needs some precision or not to store value as float
    return mCoordinate == pCoordinate.coordinate();
}

const CGPSCoordinate& CGPSCoordinate::operator =(
const CGPSCoordinate &pCoordinate)
{
    mCoordinate = pCoordinate.coordinate();
    return *this;
}

double CGPSCoordinate::toRad() const
{
    return (coordinate()/(double)COORD_1_DEGREE)/180.0*M_PI;
}

CGPSCoordinate CGPSCoordinate::fromString(const QString &pStr,
        char pPosSuffix, char pNegSuffix)
{
    if (pStr.length() < MIN_COORD_TEXT_LENGTH ||
        pStr.length() > MAX_COORD_TEXT_LENGTH)
        return CGPSCoordinate();

    QChar suffix = pStr.at(pStr.length()-1);
    if (suffix != pPosSuffix && suffix!= pNegSuffix)
        return CGPSCoordinate();

    int orientation = (suffix == pPosSuffix) ? 1 : -1;

    QStringList sl = pStr.left(pStr.length()-1).split('\260');
    if (sl.count()!=2)
        return CGPSCoordinate();

    bool ok;

    int degrees = sl.at(0).toInt(&ok);
    if (!ok)
        return CGPSCoordinate();

    double minutes = sl.at(1).toDouble(&ok);
    if (!ok)
        return CGPSCoordinate();

    return CGPSCoordinate(
            (long)(degrees*COORD_1_DEGREE + minutes*COORD_1_MINUTE) * orientation);
}

double CGPSLatitude::toRad() const
{
    assert(coordinate()>=-90*COORD_1_DEGREE && coordinate()<=90*COORD_1_DEGREE);
    return CGPSCoordinate::toRad();
}

CGPSCoordinate CGPSLatitude::fromString(const QString &pStr)
{
    return CGPSCoordinate::fromString(pStr, 'N', 'S');
}

double CGPSLongitude::toRad() const
{
    assert(coordinate()>=-180*COORD_1_DEGREE && coordinate()<=180*COORD_1_DEGREE);
    return CGPSCoordinate::toRad();
}

CGPSCoordinate CGPSLongitude::fromString(const QString &pStr)
{
    return CGPSCoordinate::fromString(pStr, 'E', 'W');
}
